package com.example.springbootdemo.config;

import com.example.springbootdemo.filter.TokenAuthFilter;
import com.example.springbootdemo.filter.TokenLoginFilter;
import com.example.springbootdemo.handler.InvalidSessionHandler;
import com.example.springbootdemo.handler.RequestAccessDeniedHandler;
import com.example.springbootdemo.handler.SessionInformationExpiredHandler;
import com.example.springbootdemo.handler.UnauthorizedEntryPointHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * 权限管理的核心配置
 * Spring Security的核心配置类是 WebSecurityConfigurerAdapter抽象类，这是权限管理启动的入口，这里我们自定义一个实现类去实现它
 * EnableWebSecurity: 开启Spring Security的功能
 * prePostEnabled属性决定Spring Security在接口前注解是否可用@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接口
 * @author hh
 * @date 2022/1/27
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private AuthenticationSuccessHandler loginSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler loginFailureHandler;

    @Autowired
    private UnauthorizedEntryPointHandler unauthorizedEntryPointHandler;

    @Autowired
    private InvalidSessionHandler invalidSessionHandler;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

    @Autowired
    private RequestAccessDeniedHandler requestAccessDeniedHandler;

    @Autowired
    private SessionInformationExpiredHandler sessionInformationExpiredHandler;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * http请求拦截配置
     * @param http http
     * @throws Exception Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用防CSRF攻击
        http.cors().and().csrf().disable();

        http
                // 1. 配置权限认证
                .authorizeRequests()
                // 配置不拦截路由
                .antMatchers(
                        "/swagger-ui.html",
                        "/swagger-resources/**",
                        "/webjars/**",
                        "/*/api-docs",
                        "/login",
                        "/websocket/*",
                        "/api/user",
                        "/es/*",
                        "/file/*",
                        "/test/*")
                .permitAll()
                // 当前登录用户，只有具有admin权限才能访问这个路径
//                .antMatchers("/api/user").hasAuthority("admin")
                // 给定多个权限
//                .antMatchers("/api/user").hasAnyAuthority("admin,manager")
                // 当前用户必须有这个角色才能访问
//                .antMatchers("/api/user").hasRole("role")
                // 给定多个角色
//                .antMatchers("/api/user").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest()
                .authenticated()
                .and()

                .addFilter(new TokenAuthFilter(authenticationManager(), redisTemplate))
                .addFilter(new TokenLoginFilter(authenticationManager(), redisTemplate))

                // 2.异常处理
                .exceptionHandling()
                // 匿名用户访问无权限资源时的异常处理
                .authenticationEntryPoint(unauthorizedEntryPointHandler)
                .accessDeniedHandler(requestAccessDeniedHandler)
                .and()

                // 3. 登入
                .formLogin().permitAll()
                // .defaultSuccessUrl("/index")
                // .failureUrl("/login?error=true")
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailureHandler)
                .and()

                // 4.记住我
                .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(60)
                .userDetailsService(customUserDetailsService);
//                .and()
//
//                // 4.登出
//                .logout().permitAll()
//                .logoutSuccessHandler(logoutSuccessHandler)
//                .deleteCookies()
//                .and()
//
                // 5.会话管理(采用了JWT无状态服务，需要将session给关闭,虽然session和jwt相互并不冲突，但是为了效率考量还是关闭session比较合理，不然服务端会积累太多session id的。)
//                .sessionManagement()
//                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//                // 超时处理
//                .invalidSessionStrategy(invalidSessionHandler)
//                // 同一账号同时登录最大用户数
//                .maximumSessions(1)
//                .expiredSessionStrategy(sessionInformationExpiredHandler);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 基于内存的方式，创建两个用户admin/123456，user/123456
         auth.inMemoryAuthentication()
                 .withUser("admin")
                 .password(passwordEncoder().encode("123456"))
                 .roles("ADMIN")
                 .and()
                 .withUser("user")
                 .password(passwordEncoder().encode("123456"))
                 .roles("USER");
        // 从数据库中读取的用户进行身份验证
        auth.userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }

//    @Override
//    public void configure(WebSecurity web) throws Exception {
//        // 不进行认证的路径，可以直接访问
//        web.ignoring().antMatchers("/api/**");
//    }

    /**
     * 指定加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }


}
